跳到主要内容

SpringBoot 整合 MyBatis

配置连接池

注意:如果使用的是 MyBatis-Plus 或者 mybatis-spring-boot-starter 则默认会导入 spring-boot-starter-jdbc 不过一般使用的都是这两个,所以可以不用配置连接池了~

先引入数据连接池依赖

添加 JDBC 启动器,引入这个启动器就自动会使用 Spring 的 hikari 数据库连接池(默认的)

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>

或者使用 druid 数据连接池:参考文档 Druid Spring Boot Starter

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>

添加数据库驱动

因为 SpringBoot 并不知道使用的数据库是什么,所以需要添加 MySQL 的驱动

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

配置连接池

前面那种自定义写配置文件读取的方式现在可以改了,导入这个启动器之后就可以直接使用定义好的 SpringProperties 配置项

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_test?useUnicode=true&characterEncoding=utf8&useSSL=true&useServerPrepStmts=true
username: root
password: root

配置环境

官方文档 使用 MyBatis 的启动器(非 Spring 官方,只是 MyBatis 自己出的)

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>

配置文件上加上 mybatis 的配置

mybatis:
type-aliases-package: com.alsritter.pojo # 实体类别名包路径,配了这个就无需在写全类名了
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 设置日志为后台输出

在启动器加上一个 @MapperScan 注解会把这个包下所有的接口都编译成响应的实现类(如果不想这样扫描包,可以继续使用 @Mapper 注解)

加上这个注解之后就无需在每个 Mapper 上加 @Mapper 注解了

@SpringBootApplication
@MapperScan("com.alsritter.mapper")
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
}

甚至可以扫描多个包

@MapperScan({"com.alsritter.mapper","com.alsritter.temp"})  

使用 XML 配置 Mapper

如果是普通开发其实可以只使用注解,但是一些动态 SQL 需要在配置文件上配置,否则很麻烦 补个 SQL 命令:查看表结构 DESC table_name

所以还是需要用到 XML 来配置(两个可以混起来使用)

首先配置 yml

mybatis:
type-aliases-package: com.alsritter.pojo # 实体类别名包路径,配了这个就无需在写全类名了
mapper-locations: classpath:mapper/*.xml

在 resources 目录下创建 mapper 目录,里面写 mapper 文件

image.png

编写接口(还是需要使用接口,因为需要自动注入,只用 xml 文件的方式太极端了)

public interface UserMapper {
// 可以和 注解 混搭使用
@Select("select * from tb_user")
List<User> getUserList();

@Transactional
int insertUser(User user);
}

编写 mapper 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--这里写接口的包名-->
<mapper namespace="com.alsritter.mapper.UserMapper">
<insert id="insertUser" parameterType="User">
insert into springboot_test.tb_user
<trim prefix="(" suffix=")" suffixOverrides=",">
user_name,
password,
<if test="age != null">
age,
</if>
<if test="sex != null">
sex,
</if>
<if test="birthday != null">
birthday,
</if>
<if test="note != null">
note,
</if>
<if test="created != null">
created,
</if>
<if test="updated != null">
updated,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
#{userName},
#{password},
<if test="age != null">
#{age},
</if>
<if test="sex != null">
#{sex},
</if>
<if test="birthday != null">
#{birthday},
</if>
<if test="note != null">
#{note},
</if>
<if test="created != null">
#{created},
</if>
<if test="updated != null">
#{updated},
</if>
</trim>
</insert>
</mapper>

Service 层

只需加上 @Transactional 注解就能自动回滚了

@Service
public class UserService {

private UserMapper userMapper;

@Resource
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}

// 新增保存用户(因为新增用户可能会失败,所以需要使用事务进行回滚)
@Transactional
public void insertUser(User user) {
userMapper.insertUser(user);
}
}

测试类

Mapper 层

public interface UserMapper {
@Select("select * from tb_user")
List<User> getUserList();
}

Service 层

参考资料 springboot整合mybatis mapper注入时报错could not autowire,几种解决方案

注意:这里有个 IDEA 的 BUG,就是会显示找不到 UserMapper 的错,但是实际可以运行

可以把 @Autowired 改成 @Resource,绕过 IDEA 的检测机制就不会报错了

@Service
public class UserService {

private UserMapper userMapper;

@Autowired
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}

// 新增保存用户(因为新增用户可能会失败,所以需要使用事务进行回滚)
@Transactional
public void saveUser(User user) {
userMapper.getUserList().forEach(System.out::println);
}
}

Controller 层

@Slf4j
@RequestMapping("/hello")
@RestController
public class HelloController {


private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}

@GetMapping("/temp1")
public String hello() {
userService.saveUser(new User());
return "hot update";
}
}

单元测试

@SpringBootTest
class StudyApplicationTests {

@Autowired
private HelloController helloController;

@Test
void contextLoads() {
helloController.hello();
}

}

事务配置

只需加上 @Transactional 注解就能自动回滚了

注意:事务捕获异常 参看 Spring02 笔记

// 新增保存用户(因为新增用户可能会失败,所以需要使用事务进行回滚)
@Transactional
public int insertUser(User user) {
int i = 0 ;
try {
i = userMapper.insertUser(user);
} catch (Exception e) {
log.warn(e.getMessage());

// 注意,需要手动强制回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return i;
}

注解结果集映射

注解 @Results@Result 来结果集映射

<mapper namespace="data.UserMapper">
<resultMap type="data.User" id="userResultMap">
<!-- 用id属性来映射主键字段 -->
<id property="id" column="user_id"/>
<!-- 用result属性来映射非主键字段 -->
<result property="userName" column="user_name"/>
</resultMap>
</mapper>

使用注解的形式


public interface AdminMapper {

@Results(id = "admin", value = {
@Result(column = "work_id", property = "id"),
@Result(column = "join_date", property = "joinDate")

})
@Select("select * from ADMIN_TB where work_id=#{workId} and password=#{password}")
Admin getAdmin(String workId, String password);

@ResultMap("admin")
@Select("select * from ADMIN_TB where work_id=#{workId}")
Admin getAdminSelf(String workId);

// 用来把管理员 id 更新到 redis
@Select("select work_id from ADMIN_TB")
List<String> getWorkerIdList();

int updateUser(Admin admin);
}